/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "ApplicationWindowedWin.h"
#include <win32/win32.h>
#include <animate/Soul.h>
#include <stdlib.h>

#include <string>
#include <Converter.h>

#include <input/hard/windows/Creator.h>

#include <threads/Lock.h>
#include <threads/threads.h>
#include <Gluck.h>

#include <WindowImpl.h>
#include <safe_new.h>
#include <win32/MiniDump.h>

namespace liba
{

#define WM_SYNCHRONIZE (WM_USER+1)

ApplicationWindowed::ApplicationWindowed(const std::string & caption, const std::string & cmd_line)
:	caption( caption ),
	active( false ),
	termination_requested(false),
	WindowMessageListener( this )
{
	init_dump(caption);

	SetWindowText( get_window()->get_wnd(), caption.c_str() );
}

ApplicationWindowed::ApplicationWindowed(const std::string & caption, int sx, int sy, bool resizeable)
:	WindowCreator( sx, sy, resizeable ),
	caption( caption ),
	active( false ),
	termination_requested(false),
	WindowMessageListener( this )
{
	init_dump(caption);

	SetWindowText( get_window()->get_wnd(), caption.c_str() );
}

ApplicationWindowed::~ApplicationWindowed()
{
	termination_requested = true;
	done_dump();
}

void ApplicationWindowed::set_property( const std::string & nam, int val )
{
	SetProp( get_window()->get_wnd(), nam.c_str(), reinterpret_cast<HANDLE>(val) );
}

void ApplicationWindowed::set_focus()
{
	SetForegroundWindow( get_window()->get_wnd() );
	SetActiveWindow( get_window()->get_wnd() );
	SetFocus( get_window()->get_wnd() );
}

int ApplicationWindowed::start()
{
	ShowWindow( get_window()->get_wnd(), true );
	UpdateWindow( get_window()->get_wnd() );

	__int64 old_tick_count = threads::get_precise_tick_count();

	bool quitting = false;

	MSG msg;
	while(true && !termination_requested) //-V560
	{
		if( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) 
		{
			if( !GetMessage( &msg, NULL, 0, 0 ) ) 
			{
				break ;
			}
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		} 
		else
		{
			if( quitting )
				continue;

			__int64 now_tick_count = threads::get_precise_tick_count();
			__int64 dt = now_tick_count - old_tick_count;
			old_tick_count = now_tick_count;

			time_float delta_time = static_cast<time_float>(dt * threads::get_precise_tick_time());//(dt < 500) ? dt : 500;

			if( !LifeCycle( delta_time ) )
			{
				quitting = true;
				SendMessage( get_window()->get_wnd(), WM_CLOSE, 0, 0 );
			}

			render();
		}
	}

	return 190106783;
}

bool ApplicationWindowed::LifeCycle(time_float delta_time )
{
	animate::Soul::Creator::LifeCycle( delta_time );
	return true;
}

void ApplicationWindowed::Synchronize(const boost::function<void ()>& fn)
{
	boost::function<void ()>* dynFn = new boost::function<void ()>(fn);
	HANDLE ev = CreateEvent(NULL, FALSE, FALSE, NULL);
	PostMessage(
		get_window()->get_wnd(),
		WM_SYNCHRONIZE,
		reinterpret_cast<WPARAM>(ev),
		reinterpret_cast<LPARAM>(dynFn));
	WaitForSingleObject(ev, INFINITE);
}

void ApplicationWindowed::Schedule(const boost::function<void ()>& fn)
{
	boost::function<void ()>* dynFn = new boost::function<void ()>(fn);
	PostMessage(
		get_window()->get_wnd(),
		WM_SYNCHRONIZE,
		0,
		reinterpret_cast<LPARAM>(dynFn));
}

void handleSynchronization(boost::function<void ()>* fn, HANDLE ev)
{
	std::auto_ptr<boost::function<void ()> > fnDtor(fn);
	(*fn)();
	if (ev)
		SetEvent(ev);
}

int ApplicationWindowed::window_proc( unsigned msg, unsigned wParam, unsigned lParam )
{
	int x_pos = LOWORD( lParam );
	int y_pos = HIWORD( lParam );
	switch (msg) 
	{
	case WM_SYNCHRONIZE:
		handleSynchronization(
			reinterpret_cast<boost::function<void ()>*>(lParam),
			reinterpret_cast<HANDLE>(wParam));

		break;
	case WM_ACTIVATE:
		{
			int active_flag = LOWORD(wParam);
			int minimized_flag = (BOOL) HIWORD(wParam); // minimized flag 

			switch( active_flag )
			{
			case WA_ACTIVE:
			case WA_CLICKACTIVE:
				active = true;
///				resume();
				break;
			case WA_INACTIVE:
				active = false;
///				suspend();
				break;
			}
		}
		break;
	case WM_PAINT:
		{
			PAINTSTRUCT paint;
			HDC hdc = BeginPaint( get_window()->get_wnd(), &paint );
			EndPaint( get_window()->get_wnd(), &paint );

///			render();
		}
		return 0;
	case WM_CLOSE:
		PostMessage( get_window()->get_wnd(), WM_QUIT, 0, 0 );
		return 0;
	case WM_SIZE:
		{
			RECT rect;
			GetClientRect( get_window()->get_wnd(), &rect );
			input::hard::windows::Creator::resize( rect.bottom - rect.top, rect.right - rect.left );
		}
		break;
	case WM_DESTROY:
		return 0;
	case WM_KEYDOWN:
		input::hard::windows::Creator::key_down( wParam );
		break;
	case WM_KEYUP:
		input::hard::windows::Creator::key_up( wParam );
		break;
	case WM_CHAR:
		input::hard::windows::Creator::key_char( wParam );
		break;
	case WM_MOUSEMOVE:
		input::hard::windows::Creator::mouse_move( x_pos, y_pos );
		break;
	case WM_SETCURSOR:
		if( LOWORD(lParam) == HTCLIENT )
		{
			SetCursor( 0 );
			return TRUE;
		}
		break;
	case WM_LBUTTONDOWN:
		input::hard::windows::Creator::mouse_down( 0 );
		break;
	case WM_LBUTTONUP:
		input::hard::windows::Creator::mouse_up( 0 );
		break;
	case WM_RBUTTONDOWN:
		input::hard::windows::Creator::mouse_down( 1 );
		break;
	case WM_RBUTTONUP:
		input::hard::windows::Creator::mouse_up( 1 );
		break;
	}
	return WindowMessageListener::window_proc( msg, wParam, lParam);
}

} // namespace liba

